#include<iostream>
#include<string>
#include<stack>
#include<vector>
#include<regex>
#include "operands.h"
using namespace std;

regex re_double("-?(([1-9]\\d*\\.\\d*)|(0\\.\\d*[1-9]\\d*))");
regex re_int("-?(0|[1-9]\\d*)");

double convert(string str){
    istringstream istr(str);
    double i;
    istr >> i;
    return i;
}

string addBlank(string& str){
    string new_str;
    int index = 0;
    for(char i: str){
        if((i<'0' || i>'9') && i!='.'){
            string j = " ";
            j[0] = i;
            new_str = new_str + " " + j + " ";
        }else{
            string j = " ";
            j[0] = i;
            new_str += i;
        }
        index ++;
    }
    str = new_str;
    // cout << "addBlank: " << str << endl; 
    return str;
}

string neg2pos(string& str)
{
    int index = 0;
    string new_str;
    for(auto i:str)
    {
        if(i=='-')
        {
            if(index == 0){
                new_str += string("0-");
            }else if(str.at(index-1) == '('){
                new_str += string("0-");
            }else{
                new_str +=i;
            }
        }else{
            new_str += i;
        }
        index++;
    }
    str = new_str;
    // cout << "neg2pos: " << str << endl;
    return str;
}

string trim(string raw){
    // 去除无关字符
	string trimmed = "";
    for(auto x:raw)
    {           
        if(char(x) <='9' && char(x)>='0'){
            trimmed += x;
        }else if(char(x)=='-' || char(x)=='+' || char(x)=='*' || char(x)=='/' || char(x)=='(' 
        || char(x) == ')' || char(x) == '.'){
            trimmed += x;
        }
    }
    //neg2pos
    neg2pos(trimmed);
    //addBlank
    addBlank(trimmed);
    return trimmed;
}

bool checkNum(string& str){
    return regex_match(str, re_double) || regex_match(str, re_int);
}

bool checkOperand(string& str){
    const char* chr = str.data();
    if(*chr != '+' && *chr != '-' && *chr != '/' && *chr != '*')
        return false;
    return true;
}

void calculate(stack<double> &num_stk, stack<string> &sym_stk){
    double num1;
    double num2;
    string operon;
    //pop出两个数字和一个操作符进行计算后将结果压入栈中
    if(num_stk.size()<=1||sym_stk.empty())
        throw 0;
    num1 = num_stk.top();
    num_stk.pop();
    num2 = num_stk.top();
    num_stk.pop();
    operon = sym_stk.top();
    sym_stk.pop();
    operands operand(operon);
    cout << num2 << " ";
    cout << operon << " ";
    cout << num1 << " ";
    cout << " = " << operand.operator()(num2, num1) << endl;
    num_stk.push(operand.operator()(num2, num1));
}

int priority(string str){
    if(str == "+" || str == "-"){
        return 1;
    }else if(str == "*" || str == "/"){
        return 2;
    }
    return 0;
}

double calculator(string formula){
    stack<string> sym_stk;
    stack<double> num_stk;
    string i;
    double sum;
    stack<double> &rnum = num_stk;
    stack<string> &rsym = sym_stk;
    cout << "Formula: " << formula << endl;
    istringstream sstr(formula);
    while(sstr >> i){
        if(checkNum(i)){
            rnum.push(convert(i));
        }else if(rsym.empty()){
            rsym.push(i);
        }else{
            if(priority(rsym.top()) < priority(i) && i != "(" && i != ")"){
                rsym.push(i);
            }else if(priority(rsym.top()) >= priority(i) && i != "(" && i != ")"){
                do{
                    calculate(rnum, rsym);
                    if(rsym.empty()){
                        break;
                    }else if(rsym.top()=="("){break;}
                }while( priority(rsym.top()) >= priority(i) );
                rsym.push(i);
            }else if(i=="("){
                rsym.push(i);
            }else if(i==")"){
                while(rsym.top()!="(")
                    calculate(rnum, rsym);
                rsym.pop();
            }
        }
    }
    while(!rsym.empty()){
        calculate(rnum, rsym);
    }
    return rnum.top();
}

int simulate(string formula){
    if(trim(formula) == "")
        throw 0;
    cout << calculator(trim(formula)) << endl;
    cout << "=============================================" << endl;
}